;-------------------------------------------------------------------------------
format ELF64 executable 3
entry start

SDL_INIT_VIDEO = 0x00000020
SDL_OPENGL = 0x04000000
SDL_NOFRAME = 0x02000000
SDL_KEYDOWN = 0x300

GL_FRAGMENT_SHADER = 0x8B30

WIN_W = 800
WIN_H = 600

DEBUG = 0

macro emit [param] {param}
;-------------------------------------------------------------------------------
include 'import64.inc'
interpreter '/lib64/ld-linux-x86-64.so.2'
needed 'libdl.so.2'
import dlopen,dlsym
;-------------------------------------------------------------------------------
segment readable writeable executable

start:
    mov rbp,rsp
    sub rsp,2048
    ; define local variables
    emit\
        <glUniform1f equ rbp-8>,<glRecti equ rbp-16>,\
        <SDL_PollEvent equ rbp-24>,<SDL_GetTicks equ rbp-32>,\
        <SDL_GL_SwapBuffers equ rbp-40>,\
        <event equ rbp-88>,\
        <log equ rbp-2048>

    mov r13,[dlsym]
if DEBUG = 1
    ; load libc
    emit <mov rdi,g_str_libc>,<xor esi,esi>,<inc esi>,<call [dlopen]>,<mov [g_libc],rax>
    emit <mov rdi,[g_libc]>,<mov rsi,g_str_printf>,<call r13>,<mov [printf],rax>
end if
    ; load SDL library
    emit <mov rdi,g_str_libsdl>,<xor esi,esi>,<inc esi>,<call [dlopen]>,<mov r12,rax>
    ; SDL_Init
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_Init>,<call r13>
    emit <mov edi,SDL_INIT_VIDEO>,<call rax>
    ; SDL_SetVideoMode
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_SetVideoMode>,<call r13>
    emit <mov edi,WIN_W>,<mov esi,WIN_H>,<xor edx,edx>,<mov ecx,SDL_OPENGL+SDL_NOFRAME>,<call rax>
    ; load needed SDL functions
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_PollEvent>,<call r13>,<mov [SDL_PollEvent],rax>
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_GL_SwapBuffers>,<call r13>,<mov [SDL_GL_SwapBuffers],rax>
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_GL_GetProcAddress>,<call r13>,<mov rbx,rax>
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_GetTicks>,<call r13>,<mov [SDL_GetTicks],rax>
    ; load needed GL commands
    emit <mov rdi,g_str_glUniform1f>,<call rbx>,<mov [glUniform1f],rax>
    emit <mov rdi,g_str_glRecti>,<call rbx>,<mov [glRecti],rax>
    ; create GLSL program
    emit <mov rdi,g_str_glCreateProgram>,<call rbx>,<call rax>,<mov r15d,eax>
    ; create and attach fragment shader
    emit <mov rdi,g_str_glCreateShader>,<call rbx>
    emit <mov edi,GL_FRAGMENT_SHADER>,<call rax>,<mov r14d,eax>
    emit <mov rdi,g_str_glShaderSource>,<call rbx>
    emit <mov edi,r14d>,<mov esi,1>,<mov rdx,g_src_fs_ptr>,<xor ecx,ecx>,<call rax>
    emit <mov rdi,g_str_glAttachShader>,<call rbx>
    emit <mov edi,r15d>,<mov esi,r14d>,<call rax>
    emit <mov rdi,g_str_glCompileShader>,<call rbx>
    emit <mov edi,r14d>,<call rax>
    ; link program
    emit <mov rdi,g_str_glLinkProgram>,<call rbx>
    emit <mov edi,r15d>,<call rax>
    ; use program
    emit <mov rdi,g_str_glUseProgram>,<call rbx>
    emit <mov edi,r15d>,<call rax>
if DEBUG = 1
    ; print GLSL info log
    emit <mov rdi,g_str_glGetProgramInfoLog>,<call rbx>
    emit <mov edi,r15d>,<mov esi,1024>,<xor edx,edx>,<lea rcx,[log]>,<call rax>
    emit <mov rdi,g_str_log>,<lea rsi,[log]>,<call [printf]>
end if
    ; get <t> uniform location
    emit <mov rdi,g_str_glGetUniformLocation>,<call rbx>
    emit <mov edi,r15d>,<mov rsi,g_str_time>,<call rax>,<mov r14d,eax>

      .main_loop:
    ; process window events
    emit <lea rdi,[event]>,<call qword [SDL_PollEvent]>
    cmp dword [event],SDL_KEYDOWN
    je .main_loop_end
    ; get time
    call qword [SDL_GetTicks]
    ; set <t> uniform
    emit <cvtsi2ss xmm0,eax>,<divss xmm0,[g_ticks_per_sec]>,<mov edi,r14d>,<call qword [glUniform1f]>
    ; draw
    emit <mov edi,-1>,<mov esi,-1>,<mov edx,1>,<mov ecx,1>,<call qword [glRecti]>
    ; swap
    call qword [SDL_GL_SwapBuffers]
    jmp .main_loop
      .main_loop_end:

    ; SDL_Quit
    emit <mov rdi,r12>,<mov rsi,g_str_SDL_Quit>,<call r13>,<call rax>
    ; terminate process
    emit <mov eax,60>,syscall
;-------------------------------------------------------------------------------
align 1
g_src_fs:
db  'uniform float t;'
db  'void main(){'
db      'gl_FragColor=vec4(.5);'
db  '}',0

g_str_libsdl db 'libSDL-1.3.so.0',0
g_str_SDL_Init db 'SDL_Init',0
g_str_SDL_SetVideoMode db 'SDL_SetVideoMode',0
g_str_SDL_PollEvent db 'SDL_PollEvent',0
g_str_SDL_GL_SwapBuffers db 'SDL_GL_SwapBuffers',0
g_str_SDL_GL_GetProcAddress db 'SDL_GL_GetProcAddress',0
g_str_SDL_GetTicks db 'SDL_GetTicks',0
g_str_glCreateShader db 'glCreateShader',0
g_str_glShaderSource db 'glShaderSource',0
g_str_glCompileShader db 'glCompileShader',0
g_str_glAttachShader db 'glAttachShader',0
g_str_glUniform1f db 'glUniform1f',0
g_str_glRecti db 'glRecti',0
g_str_glCreateProgram db 'glCreateProgram',0
g_str_glLinkProgram db 'glLinkProgram',0
g_str_glUseProgram db 'glUseProgram',0
g_str_glGetUniformLocation db 'glGetUniformLocation',0
g_str_time db 't',0
g_str_SDL_Quit db 'SDL_Quit',0
if DEBUG = 1
g_str_libc db 'libc.so.6',0
g_str_printf db 'printf',0
g_str_glGetProgramInfoLog db 'glGetProgramInfoLog',0
g_str_log db 'GLSL info log:',10,'%s',10,0
g_str_d db '%d',10,0
g_str_p db '%p',10,0
end if

align 4
g_ticks_per_sec dd 1000.0

align 8
g_src_fs_ptr dq g_src_fs

if DEBUG = 1
g_libc dq 0
printf dq 0
end if
;-------------------------------------------------------------------------------
